from base.util import *


class Node:
    def __init__(self, indented_line):
        self.children_s = []
        self.level = len(indented_line)-len(indented_line.lstrip())
        self.text = indented_line.strip()

    def add_children(self, nodes):
        child_level = nodes[0].level
        while nodes:
            node = nodes.pop(0)
            if node.level == child_level:  # add node as a child
                self.children_s.append(node)
            elif node.level > child_level:  # add nodes as grandchildren of the last child
                nodes.insert(0, node)
                self.children_s[-1].add_children(nodes)
            elif node.level <= self.level:  # this node is a sibling, no more children
                nodes.insert(0, node)
                return

    def as_dict(self):
        if len(self.children_s) > 1:
            # return {self.text: [node.as_dict() for node in self.children]}
            return {self.text: map_list(Node.as_dict, self.children_s)}
        elif len(self.children_s) == 1:
            # return {self.text: self.children[0].as_dict()}
            return {self.text: map_list(Node.as_dict, self.children_s)}

        else:
            return self.text


class SplitMultiLayer:
    """拆分: 嵌套层级缩进 -> 降维拆分"""

    def to_dict(self, indented_text):
        root = Node('root')
        root.add_children([Node(line)
                           for line in indented_text.splitlines()
                           if line.strip()])
        d = root.as_dict()['root']
        return d

    def nested_dict_iter(self, nested):
        if isinstance(nested, dict):
            for key, value in nested.items():
                yield key, value
                yield from self.nested_dict_iter(value)
        elif isinstance(nested, (list, tuple)):
            for i in nested:
                yield from self.nested_dict_iter(i)

    def main(self, s):
        my_dict = self.to_dict(s)
        result = self.parse(my_dict)
        return result.rstrip()

    def parse(self, tree):
        result = ""
        # k: str
        # v: List[Union[dict, str]]
        for k, v in self.nested_dict_iter(tree):
            # 如果分支大于1, 则说明是汇总卡片, 于是加上汇总前缀
            prefix = "Σ " if (len(v) >= 2) else ""
            result += prefix+k+"\n"
            # pp()(k, v)
            children = []
            for i in v:
                if type(i) == dict:
                    children.append(list(i.keys())[0])
                else:
                    children.append(i)

            for child in children:
                result += "    "+child+"\n"
                # print("   ", child)

        return result


class HandleItemsCard:
    """处理有多项内容(集合/枚举)的anki卡片, 拆分为多张
    不好用, 必须前后都有提醒才行
    """

    def __init__(self, s):
        self.title = ""
        self.li = []
        self.regex_fetch(s)

    def mark_li(self, li: List, sep=3):
        n = len(li)
        if n < sep:
            yield li, f"前{n}项"
            return
        if n == sep:
            yield li[0:2], f"前两项"
            yield li[1:n], f"从\"{li[1]}\"开始的两项"
            return
        for i in range(0, n):
            if i == 0:
                doc = f"前3项"
            else:
                doc = f"从\"{li[i]}\"开始的3项"
            end = i+sep
            if end > n:
                break
            yield li[i:end], doc

    def parse(self):
        li = self.mark_li(self.li)
        for es, doc in li:
            path1 = self.title+" "+doc
            path2 = "\n"+"\n".join("    "+e for e in es)
            yield path1+path2

    def regex_fetch(self, s):
        """
        """
        title, s = re.search(r"(?s)(.+?)((?:\s{4}|\t).+)", s).groups()
        self.title = title.strip()
        li = re.split(r"\n+", s.strip())
        self.li = map_list(str.strip, li)

    def __call__(self, *args, **kwargs):
        return "\n".join(self.parse()).strip()+"\n"


class Anki:
    """说明
    通过相邻元素来 分隔卡片
    通过缩进的数量来 分隔字段

    缩进: \t 或者 空格*4
    最大字段数: 5个
    注意: 每次改变缩进,都会创建新的字段. 所以卡片内的缩进必须逐渐增大
    """

    def __init__(self, s: str, tag="", get_field_2=False):
        # "符号(标识)"
        self.prefix_s = ["\n1.|\n", "\n3.|\n",
                         "\n4.|\n", "\n5.|\n", "\n6.|\n"]
        self.postfix_card = "\n.+\n"
        self.s = s.replace("\r", "").strip()
        self.tag = tag
        self.s, self.field_2 = self.make_firstLine(get_field_2)
        self.li_block = []

    def __call__(self):
        """导入, 调试, 返回值"""
        # 处理标签, 空字符串
        s, tag, = self.s, self.tag
        if not s.strip():
            return "err: 失败, 字符串为空"
        tag = tag if tag.strip() else "默认"

        with context(self.block_repl, self.block_restore, s) as obj:
            s = obj.x
            res = self.main(s)
            #  拼接标签, 还原缩进
            res = f".a#{tag}.a#"+"".join(res).replace(" "*4, "")
            res = self.add_field2(res)
            obj.x = res
        return obj.x

    def block_repl(self, s: str):
        """ 处理代码块 或 不拆分缩进
        """
        li_code = self.li_block
        count = -1

        def repl(m: Match):
            nonlocal count
            count += 1
            g1 = m.group(1)
            li_code.append(ustr(g1).indent_back())
            return f"<<<{count}>>>"

        s = re.sub(r"(?s)<<<(.*?)>>>", repl, s)
        return s

    def block_restore(self, s):
        s = re.sub(r"<<<(\d+)>>>",
                   lambda m: self.li_block[int(m.group(1))], s)
        return s

    def make_firstLine(self, get_field_2):
        """
        取第一行(也就是取最开始的问题)作为第二个字段(分类)
        """
        s = self.s
        if get_field_2:
            first_line = s.splitlines()[0].strip()
            s = (re.sub(r".+\n", "", s, count=1)) if \
                (re.search(r"^(?:《(.*?)》|(.*?)《》)$", first_line)) \
                else s
            field_2 = ustr(first_line).replaceMore("《》", "")
            return s, field_2
        else:
            return s, ""

    def add_field2(self, res):
        """加上第二个字段"""
        if not self.field_2:
            return res
        field_2 = "\n2.|\n"+ustr(self.field_2).put_pair("《》")
        repl = lambda m: field_2+m.group()
        res = re.sub(r"\n\.\+\n\n", repl, res)
        return res+field_2

    def to_yield(self, n1, sub_li, postfix_=False, e1=None):
        """前缀 内容 后缀"""
        prefix_s = (n1 < len(self.prefix_s) and
                    self.prefix_s[n1] or "\n")
        postfix_card = (postfix_ and
                        self.postfix_card or "")
        text = "".join((e1
                        and chain(sub_li, [e1]) or sub_li)).rstrip()
        return "".join([prefix_s, text, postfix_card])

    def get_indent(self, li):
        """获取缩进数量, 如果是空行则使用上一个缩进
        """
        li, n = iter(li), 0
        yield next(li), n
        for e in li:
            e, n = e.strip() and \
                   (e, len(re.search(r"^ *", e).group())//4) or \
                   (e, n)
            yield e, n

    def main(self, s: str):
        if "分隔,筛选,定义":
            space4 = " "*4
            sub_li = []
            li = s.strip().replace("\t", space4).splitlines(True)
        if "函数封装":
            def swap():
                nonlocal e1, n1
                e1, n1 = e2, n2

            is_new_card = lambda: n1 > 0 and n2 == 0
            is_new_field = lambda: n2 > n1
        li = self.get_indent(li)
        e1, n1 = next(li)
        for e2, n2 in li:
            sub_li.append(e1)
            if is_new_card():  # 如果是新卡片
                yield self.to_yield(n1, sub_li, True)
                sub_li, n1 = [], 0
            else:
                if is_new_field():  # 如果是新字段
                    yield self.to_yield(n1, sub_li)
                    sub_li, n1 = [], n1+1
            swap()
        yield self.to_yield(n1, sub_li, e1=e1)


# print(Anki(s, tag_select, field_2)())

if __name__ == "__main__":
    s = """
1
    21
        31
            41
            42
    22
        32
        33
    """
    # s = """
    # 1
    #     21
    # """
    _ = SplitMultiLayer().main(s)
    pp()(_)
    # csv_test(func1, is_txt=1)
